package pt.inescid.components.policyengine.heimdall.pap;

import java.io.StringReader;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

import pt.inescid.components.policyengine.heimdall.base.User;
import pt.inescid.components.policyengine.heimdall.commons.Log;
import pt.inescid.components.policyengine.heimdall.pap.base.CesAggDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.CesDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.CompositeDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.DomainDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.EventFieldDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.ExternalMethodDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.ModelDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.PolicyDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.PurgeDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.QuantifierDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.ReevaluationDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.RuleDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.SimpleDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.SubPolicyDefinition;
import pt.inescid.components.policyengine.heimdall.pdp.base.PurgeInstance;

public class PolicyParser extends DefaultHandler {

    private ModelDefinition currentModelDef = null;
    private CesDefinition currentCes = null;

    private PolicyDefinition currentPolicy = null;
    private int policySection;

    private RuleDefinition currentExp = null;
    private RuleDefinition previousLevelExp = null;
    
    private ReevaluationDefinition currentReeval = null;
    private PurgeDefinition currentPurge = null;
    private boolean insidePrivacy = false;

    private User currentUser = null;
    
    
    
	public PolicyParser(User pUser, String modelTag) {
		currentUser = pUser;
		
		currentModelDef = currentUser.getModelByTag(modelTag);
		if (currentModelDef == null) {
			currentModelDef = new ModelDefinition(modelTag);
			currentUser.addModel(currentModelDef);
		}
	}; // endElement
  

    
    //===========================================================
    // SAX DocumentHandler methods
    //===========================================================

    public void startDocument()
    throws SAXException
    {
    }

    public void endDocument()
    throws SAXException
    {
    }

    /* ===========================================================
    ==============================================================
    =============                    =============================
    =============      startElement  =============================
    =============                    =============================
    ==============================================================
    =========================================================== */

    public void startElement(String namespaceURI,
                             String sName, // simple name
                             String qName, // qualified name
                             Attributes attrs)
    throws SAXException
    {
        String eName = sName; // element name
        if ("".equals(eName)) eName = qName; // not namespaceAware

// ----------------------------------------------------------
// ------------------------   usageModel   ------------------------
// ----------------------------------------------------------
        if (eName.equals("usageModel")) {
//        	currentModel = ModelDefinition.getInstance();
        }
//      ----------------------------------------------------------
//      ------------------------   domain   ------------------------
//      ----------------------------------------------------------
        else if (eName.equals("domain")) {
        	currentModelDef.addDomain(new DomainDefinition(
        			attrs.getValue("name"),
        			attrs.getValue("value"),
        				attrs.getValue("value") != null && 
        				!attrs.getValue("value").contains("ldap")));
        }
//      ----------------------------------------------------------
//      ------------------------   ces   ------------------------
//      ----------------------------------------------------------
        else if (eName.equals("ces")) {
         	currentCes = new CesDefinition(
         			attrs.getValue("name"),
        			attrs.getValue("baseCes"),
        			currentModelDef,
        			attrs.getValue("withinMilli"),
        			currentModelDef
        			);
         	currentModelDef.addCes(currentCes);
        	previousLevelExp = null;
        }
//      ----------------------------------------------------------
//      ------------------------   policy   ------------------------
//      ----------------------------------------------------------
        else if (eName.equals("policy")) {
        	// policy can exist because of a subpolicy statement
        	currentPolicy = currentModelDef.getPolicy(attrs.getValue("name"));
        	currentPolicy.setRemote(false);
        	
        	policySection = PolicyDefinition.DECISION_SECTION;
        	previousLevelExp = null;

        }
//      ----------------------------------------------------------
//      ------------------------   quant   ------------------------
//      ----------------------------------------------------------
        else if (eName.equals("quant")) {
        	try {
				currentPolicy.addQuantifier(new QuantifierDefinition(
						attrs.getValue("name"),
						attrs.getValue("ces"),
						attrs.getValue("domain"),
						attrs.getValue("type"),
						attrs.getValue("value"),
						attrs.getValue("within"),
						currentModelDef));
			} catch (Exception e) {
				e.printStackTrace();
				System.exit(-1);
			}
        }
//      ----------------------------------------------------------
//      ------------------------   relevance   ------------------------
//      ----------------------------------------------------------
        else if (eName.equals("relevance")) {
        	policySection = PolicyDefinition.RELEVANCE_SECTION;
        }
//      ----------------------------------------------------------
//      ------------------------   decision   ------------------------
//      ----------------------------------------------------------
        else if (eName.equals("decision")) {
        	policySection = PolicyDefinition.DECISION_SECTION;
        }
//      ----------------------------------------------------------
//      ------------------------   reevaluation   ------------------------
//      ----------------------------------------------------------
        else if (eName.equals("reevaluation")) {
        	currentReeval = new ReevaluationDefinition(
        			attrs.getValue("action"));
        	
        	currentModelDef.addReevaluation(currentReeval);
        }
//      ----------------------------------------------------------
//      ------------------------   reevalStart   ------------------------
//      ----------------------------------------------------------
        else if (eName.equals("reevalStart")) {
        	policySection = PolicyDefinition.REEVAL_START_SECTION;
        }
//      ----------------------------------------------------------
//      ------------------------   reevalEvery   ------------------------
//      ----------------------------------------------------------
        else if (eName.equals("reevalEvery")) {
        	currentReeval.setEvery(
        			attrs.getValue("period"));
        }
//      ----------------------------------------------------------
//      ------------------------   reevalStop   ------------------------
//      ----------------------------------------------------------
        else if (eName.equals("reevalStop")) {
        	policySection = PolicyDefinition.REEVAL_STOP_SECTION;
        }
//      ----------------------------------------------------------
//      ------------------------   reevalStop   ------------------------
//      ----------------------------------------------------------
        else if (eName.equals("reevalAction")) {
        	currentReeval.setAction(
        			attrs.getValue("http"));
        }
//      ----------------------------------------------------------
//      ------------------------   purge   ------------------------
//      ----------------------------------------------------------
        else if (eName.equals("purge")) {
        	currentPurge = new PurgeDefinition(
        			attrs.getValue("period"));
        	
        }
//      ----------------------------------------------------------
//      ------------------------   privacy   ------------------------
//      ----------------------------------------------------------
        else if (eName.equals("privacy")) {
        	insidePrivacy = true;
        	
        }
//      ----------------------------------------------------------
//      ------------------------   operation/condition   ------------------------
//      ----------------------------------------------------------
        else if (eName.equals("operation") || eName.equals("condition")) {
 
        	String expressionType = attrs.getValue("type");
        	currentExp = null;
       	
        	for (int i = 0; i < RuleDefinition.RULE_TYPE_CODES.length;
        					i++) {
        		
            	if (expressionType.equals(
            			RuleDefinition.RULE_TYPE_CODES[i])) {
            		currentExp = new CompositeDefinition(
            				i,
            				previousLevelExp);
            		i = RuleDefinition.RULE_TYPE_CODES.length;
            	}
        	}
 
        	if (currentExp == null) {
               	System.out.println("\n-->ERRO: tag desconhecida (" + 
               			expressionType + ")");
        	}
        	
        	if (currentPolicy != null) {
        		currentPolicy.addExpression(currentExp, previousLevelExp,
            			policySection);
        	}
        	else if (policySection == 
        		PolicyDefinition.REEVAL_START_SECTION) {
        		
        		currentReeval.setStart(currentExp, previousLevelExp);
        	}
        	else if (policySection == 
        		PolicyDefinition.REEVAL_STOP_SECTION) {
        		
        		currentReeval.setStop(currentExp, previousLevelExp);
        	}
        	else if (currentPurge != null) {
        		currentPurge.setPurgeFilter(currentExp, 
        				previousLevelExp);
        	}
        	else if (insidePrivacy) {
        		currentCes.setPrivacyFilter(currentExp, 
        				previousLevelExp);
        	}
        	else if (currentCes != null) {
        		currentCes.setRestriction(currentExp, previousLevelExp);
        	}
        	
        	previousLevelExp = currentExp;
        }
//      ----------------------------------------------------------
//      ------------------------   exp   ------------------------
//      ----------------------------------------------------------
        else if (eName.equals("exp")) {
        	
        	String expressionType = attrs.getValue("type");
        	String expressionValueType = attrs.getValue("valueType");
        	String expressionValue = 
//        		Utilities.substKeywords(
        			attrs.getValue("value");
        	currentExp = null;
        	
        	if (expressionType.equals(RuleDefinition.RULE_TYPE_CODES[
        	          RuleDefinition.SUBPOLICY_EXPRESSION])) {
        		currentExp = new SubPolicyDefinition(
        				RuleDefinition.SUBPOLICY_EXPRESSION,
        				expressionValueType,
        				previousLevelExp,
        				attrs.getValue("domain"),
        				expressionValue,
        				currentModelDef);
        	}         		
        	else if (expressionType.equals(RuleDefinition.RULE_TYPE_CODES[
           	          RuleDefinition.EVENT_FIELD_EXPRESSION])) {
           		currentExp = new EventFieldDefinition(
            			currentModelDef,
           				RuleDefinition.EVENT_FIELD_EXPRESSION,
           				expressionValueType,
           				previousLevelExp,
           				expressionValue);
        	}         		
        	else if (expressionType.equals(RuleDefinition.RULE_TYPE_CODES[
				      RuleDefinition.LITERAL_EXPRESSION])) {
				currentExp = new SimpleDefinition(
						RuleDefinition.LITERAL_EXPRESSION,
						expressionValueType,
						previousLevelExp,
						expressionValue);
        	}         		
        	else if (expressionType.equals(RuleDefinition.RULE_TYPE_CODES[
				      RuleDefinition.CES_SUM_EXPRESSION])) {
				currentExp = new CesAggDefinition(
						RuleDefinition.CES_SUM_EXPRESSION,
						expressionValueType,
						previousLevelExp,
						attrs.getValue("domain"),
						attrs.getValue("ces"),
						expressionValue,
						currentModelDef
						);
        	}         		
        	else if (expressionType.equals(RuleDefinition.RULE_TYPE_CODES[
				      RuleDefinition.EXTERNAL_METHOD_EXPRESSION])) {
				currentExp = new ExternalMethodDefinition(
						RuleDefinition.EXTERNAL_METHOD_EXPRESSION,
						expressionValueType,
						previousLevelExp,
						expressionValue);
        	                                                	}         		
           	
        	if (currentExp == null) {
                   	System.out.println("\n-->ERRO: tag desconhecida (" + expressionType + ")");
        	}
        	
        	if (currentPolicy != null) {
        		currentPolicy.addExpression(currentExp, previousLevelExp,
            			policySection);
        		
        		if (currentExp.getType() == RuleDefinition.CES_SUM_EXPRESSION) {
        			previousLevelExp = currentExp;
        		}
        		else if (previousLevelExp != null && 
        				previousLevelExp.getType() == 
        			RuleDefinition.CES_SUM_EXPRESSION) {
        			
        			previousLevelExp = previousLevelExp.getPreviousLevel();
        		}
        	}
        	else if (currentPurge != null) {
        		currentPurge.setPurgeFilter(currentExp, 
        				previousLevelExp);
        	}
        	else if (currentCes != null) {
        		currentCes.setRestriction(currentExp, 
        				previousLevelExp);
        	}
        	else if (currentReeval != null) {
        		if (policySection == 
        				PolicyDefinition.REEVAL_START_SECTION) {
        			currentReeval.setStart(currentExp, previousLevelExp);
        		}
        		else {
        			currentReeval.setStop(currentExp, previousLevelExp);
        		}
        	}
        }
//      ----------------------------------------------------------
//      ------------------------  tag desconhecida  --------------
//      ----------------------------------------------------------
        else {
        	System.out.println("\n-->ERRO: tag desconhecida (" +
        			eName + ")");
//        	System.exit(-1);
         }
    } // startElement


    /* ===========================================================
    ==============================================================
    =============                    =============================
    =============      endElement    =============================
    =============                    =============================
    ==============================================================
    =========================================================== */
    
    public void endElement(String namespaceURI,
                           String sName, // simple name
                           String qName  // qualified name
                          )
    throws SAXException
    {
        String eName = sName; // element name
        if ("".equals(eName)) eName = qName; // not namespaceAware

//      ----------------------------------------------------------
//      ------------------------   ces   ------------------------
//      ----------------------------------------------------------
        if (eName.equals("ces")) {
        	currentCes = null;
        }
//      ----------------------------------------------------------
//      ------------------------   policy   ------------------------
//      ----------------------------------------------------------
        if (eName.equals("policy")) {
        	currentPolicy = null;
        }
//      ----------------------------------------------------------
//      ------------------------   operation/condition   ------------------------
//      ----------------------------------------------------------
        if (eName.equals("operation") || eName.equals("condition")) {
        	previousLevelExp = previousLevelExp.getPreviousLevel();
        }
//      ----------------------------------------------------------
//      ------------------------   reevaluation   ------------------------
//      ----------------------------------------------------------
        if (eName.equals("reevaluation")) {
        	currentReeval = null;
        }
//      ----------------------------------------------------------
//      ------------------------   purge   ------------------------
//      ----------------------------------------------------------
        if (eName.equals("purge")) {
        	currentCes.setPurge(new PurgeInstance(currentPurge));
        	currentPurge = null;
        }
//      ----------------------------------------------------------
//      ------------------------   privacy   ------------------------
//      ----------------------------------------------------------
        if (eName.equals("privacy")) {
        	insidePrivacy = false;
        	
        }
    } // endElement

    
    
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      updateModel       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public static ModelDefinition updateModel(User currentUser, 
			String modelTag,
			String policyText) {
	   	SAXParserFactory factory = null;
    	SAXParser saxParser = null;
    	
        try {
        	// Use the default (non-validating) parser
        	factory = SAXParserFactory.newInstance();
        	saxParser = factory.newSAXParser();
        	
        	// Create an application setup parser
        	DefaultHandler handlerAppSetup = new PolicyParser(currentUser,
        			modelTag);
        	
        	saxParser.parse(new InputSource(new StringReader(policyText)), 
        			handlerAppSetup);
        	
        	currentUser.getModelByTag(modelTag).setModelAsText(policyText);

        } catch (SAXParseException spe) {
        	// Error generated by the parser
        	Log.debug(Log.ERROR, "\n** Parsing error"
        			+ ", line " + spe.getLineNumber()
					+ ", uri " + spe.getSystemId() + ":" + 
					spe.getMessage(),
					"PolicyParser.updateModel");
        } catch (Throwable spe) {
        	// Error generated by the parser
        	Log.debug(Log.ERROR, "\n** Parsing error: " +
        			spe.getMessage(),
        	"PolicyParser.updateModel");
        }
        
        return (currentUser.getModelByTag(modelTag));
	} // updateModel
}

